#ifndef _VECTORS_H
#define _VECTORS_H

/*	Vectors.h (Geometry-Part)
Usage of property:
- declare like any other member (property<type, ownerclass, readonly> Name)
- Declare 2 memberfunctions to get and set the property
- Set the appropriate functions in ctor (property[] for the getter and property() for the setter)
- The property is used like a member of the templated type

Pay attention:
- There wont be any error setting a readonly-property but it wont have any effect */

#define AddConst(t) Vector3d operator+(t& v) { return Add(v); }
#define IncAddConst(t) void operator+=(t& v) { return IncAdd(v); }
#define MultConst(t) Vector3d operator*(t& v) { return Mult(v); }
#define IncMultConst(t) void operator*=(t& v) { return IncMult(v); }

template<class T, class P, bool readonly>
class DLL_EXPORT prop
{
	typedef T (P::*getprop)();
	typedef void (P::*setprop)(T& val);
	getprop getter;
	setprop setter;
	P& obj;
public:
	prop(P& inst) : obj(inst) { }
	operator T()
	{
		return (obj.*getter)();
	}
	void operator=(T& val)
	{
		if(readonly)
			return;
		(obj.*setter)(val);
	}

	void operator=(const T& val)
	{
		if(readonly)
			return;
		(obj.*setter)((T&)val);
	}

	void operator[](getprop gp)
	{
		getter = gp;
	}
	void operator()(setprop sp)
	{
		setter = sp;
	}
};

class DLL_EXPORT Vector3d
{
	double x, y, z;
	template<typename T>
	Vector3d Add(T& v)
	{
		Vector3d ve(x + v, y + v, z + v);
		return ve;
	}

	template<typename T>
	void IncAdd(T& v)
	{
		x += v;
		y += v;
		z += v;
	}
	template<typename T>
	Vector3d Mult(T& v)
	{
		Vector3d ve(x * v, y * v, z * v);
		return ve;
	}
	
	template<typename T>
	void IncMult(T& v)
	{
		x *= v;
		y *= v;
		z *= v;
	}
	double GetX() { return x; }
	double GetY() { return y; }
	double GetZ() { return z; }
public:
	prop<double, Vector3d, true> X;
	prop<double, Vector3d, true> Y;
	prop<double, Vector3d, true> Z;
	Vector3d(double x, double y, double z) : x(x), y(y), z(z), X(*this), Y(*this), Z(*this)
	{
		X[&Vector3d::GetX];
		Y[&Vector3d::GetY];
		Z[&Vector3d::GetZ];
	}

	void operator=(Vector3d& v)
	{
		x = v.x;
		y = v.y;
		z = v.z;
	}

	Vector3d operator+(Vector3d& v)
	{
		Vector3d ve(x + v.x, y + v.y, z + v.z);
		return ve;
	}

	Vector3d operator-(Vector3d& v)
	{
		Vector3d ve(x - v.x, y - v.y, z - v.z);
		return ve;
	}

	void operator+=(Vector3d& v)
	{
		x += v.x;
		y += v.y;
		z += v.z;
	}

	bool operator==(Vector3d& v)
	{
		return (x == v.x && y == v.y && z == v.z);
	}

	bool operator!=(Vector3d& v)
	{
		return !(v == *this);
	}

	bool Collinear(Vector3d& v)
	{
		if(v == *this)
			return true;
		double tmp = (x / v.x);
		if(floor(tmp) != ceil(tmp))
			return false;
		tmp = (y / v.y);
		if(floor(tmp) != ceil(tmp))
			return false;
		tmp = (z / v.z);
		if(floor(tmp) != ceil(tmp))
			return false;
		return true;
	}

	double Scalar(Vector3d& b)
	{
		return ((b.x * x) + (b.y * y) + (b.z * z)); 
	}

	Vector3d Cross(Vector3d& b)
	{
		return Vector3d((y * b.z) - (z * b.y), (z * b.x) - (x * b.z), (x * b.y) - (y * b.x));
	}
	
	AddConst(ui64);
	AddConst(double);
	AddConst(float);
	AddConst(ui32);
	AddConst(i32);
	AddConst(i64);
	AddConst(ui16);
	AddConst(i16);
	AddConst(ui8);
	AddConst(i8);
	IncAddConst(ui64);
	IncAddConst(i64);
	IncAddConst(ui32);
	IncAddConst(i32);
	IncAddConst(ui16);
	IncAddConst(i16);
	IncAddConst(ui8);
	IncAddConst(i8);
	IncAddConst(double);
	IncAddConst(float);
	MultConst(ui64);
	MultConst(i64);
	MultConst(ui32);
	MultConst(i32);
	MultConst(ui16);
	MultConst(i16);
	MultConst(ui8);
	MultConst(i8);
	MultConst(double);
	MultConst(float);
	IncMultConst(ui64);
	IncMultConst(i64);
	IncMultConst(ui32);
	IncMultConst(i32);
	IncMultConst(ui16);
	IncMultConst(i16);
	IncMultConst(ui8);
	IncMultConst(i8);
	IncMultConst(double);
	IncMultConst(float);
};

#endif